﻿#include "Subsystem/BaseReplaySubsystem.h"
#include "Engine/DemoNetDriver.h"
#include "Runtime/NetworkReplayStreaming/NullNetworkReplayStreaming/Public/NullNetworkReplayStreaming.h"

void UBaseReplaySubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
	Super::Initialize(Collection);
	EnumerateStreamsPtr = FNetworkReplayStreaming::Get().GetFactory().CreateReplayStreamer();
	OnEnumerateStreamsCompleteDelegate = FEnumerateStreamsCallback::CreateUObject(this, &UBaseReplaySubsystem::OnEnumerateStreamsComplete);
	OnDeleteFinishedStreamCompleteDelegate = FDeleteFinishedStreamCallback::CreateUObject(this, &UBaseReplaySubsystem::OnDeleteFinishedStreamComplete);
}

void UBaseReplaySubsystem::StartRecordingReplayFromBP(FString ReplayName, FString FriendlyName)
{
	if (ReplayName.IsEmpty())
	{
		FDateTime now = FDateTime::Now();
		ReplayName = FString::Printf(TEXT("Replay_%d-%d-%d %d:%d:%d"), now.GetYear(), now.GetMonth(), now.GetDay(), now.GetHour(), now.GetMinute(), now.GetSecond());
	}
	GetGameInstance()->StartRecordingReplay(ReplayName, FriendlyName);
}

void UBaseReplaySubsystem::StopRecordingReplayFromBP()
{
	GetGameInstance()->StopRecordingReplay();
}

void UBaseReplaySubsystem::PlayReplayFromBP(FString ReplayName)
{
	GetGameInstance()->PlayReplay(ReplayName);
}

void UBaseReplaySubsystem::FindReplays()
{
	if (EnumerateStreamsPtr.Get())
	{
		// const FEnumerateStreamsCallback& Delegate
		EnumerateStreamsPtr.Get()->EnumerateStreams(FNetworkReplayVersion(), 0, FString(), TArray<FString>(), OnEnumerateStreamsCompleteDelegate);
	}
}

void UBaseReplaySubsystem::RenameReplayFriendlyName(const FString& ReplayName, const FString& NewFriendlyReplayName)
{
	FNullReplayInfo Info;

	const FString DemoPath = FPaths::Combine(*FPaths::ProjectSavedDir(), TEXT("Demos/"));
	const FString StreamDirectory = FPaths::Combine(*DemoPath, *ReplayName);
	const FString StreamFullBaseFilename = FPaths::Combine(*StreamDirectory, *ReplayName);
	const FString InfoFilename = StreamFullBaseFilename + TEXT(".replayinfo");

	TUniquePtr<FArchive> InfoFileArchive(IFileManager::Get().CreateFileReader(*InfoFilename));

	if (InfoFileArchive.IsValid() && InfoFileArchive->TotalSize() != 0)
	{
		FString JsonString;
		*InfoFileArchive << JsonString;

		Info.FromJson(JsonString);
		Info.bIsValid = true;

		InfoFileArchive->Close();
	}

	// Set FriendlyName
	Info.FriendlyName = NewFriendlyReplayName;

	// Write File Info
	TUniquePtr<FArchive> ReplayInfoFileAr(IFileManager::Get().CreateFileWriter(*InfoFilename));

	if (ReplayInfoFileAr.IsValid())
	{
		FString JsonString = Info.ToJson();
		*ReplayInfoFileAr << JsonString;

		ReplayInfoFileAr->Close();
	}
}

void UBaseReplaySubsystem::DeleteReplay(const FString& ReplayName)
{
	if (EnumerateStreamsPtr.Get())
	{
		EnumerateStreamsPtr.Get()->DeleteFinishedStream(ReplayName, OnDeleteFinishedStreamCompleteDelegate);
	}
}

bool UBaseReplaySubsystem::SetCurrentReplayPausedState(bool bDoPause)
{
	// UGameplayStatics::GetPlayerController()
	// GetGameInstance()->GetPrimaryPlayerController()
	AWorldSettings* WorldSettings = GetGameInstance()->GetPrimaryPlayerController()->GetWorldSettings();

	// Set MotionBlur off and Anti Aliasing to FXAA in order to bypass the pause-bug of both
	static const auto CVarAA = IConsoleManager::Get().FindConsoleVariable(TEXT("r.DefaultFeature.AntiAliasing"));

	static const auto CVarMB = IConsoleManager::Get().FindConsoleVariable(TEXT("r.DefaultFeature.MotionBlur"));

	if (bDoPause)
	{
		PreviousAASetting = CVarAA->GetInt();
		PreviousMBSetting = CVarMB->GetInt();

		// Set MotionBlur to OFF, Anti-Aliasing to FXAA
		CVarAA->Set(1);
		CVarMB->Set(0);

		WorldSettings->SetPauserPlayerState(GetGameInstance()->GetPrimaryPlayerController()->PlayerState);
		return true;
	}
	// Rest MotionBlur and AA
	CVarAA->Set(PreviousAASetting);
	CVarMB->Set(PreviousMBSetting);

	WorldSettings->SetPauserPlayerState(nullptr);
	return false;
}

int32 UBaseReplaySubsystem::GetCurrentReplayTotalTimeInSeconds() const
{
	if (GetWorld() && GetWorld()->GetDemoNetDriver())
	{
		return GetWorld()->GetDemoNetDriver()->GetDemoTotalTime();
	}
	return 0;
}

int32 UBaseReplaySubsystem::GetCurrentReplayCurrentTimeInSeconds() const
{
	if (GetWorld() && GetWorld()->GetDemoNetDriver())
	{
		return GetWorld()->GetDemoNetDriver()->GetDemoCurrentTime();
	}
	return 0;
}

void UBaseReplaySubsystem::SetCurrentReplayTimeToSeconds(int32 Seconds)
{
	if (GetWorld() && GetWorld()->GetDemoNetDriver())
	{
		GetWorld()->GetDemoNetDriver()->GotoTimeInSeconds(Seconds);
	}
}

void UBaseReplaySubsystem::SetCurrentReplayPlayRate(float PlayRate)
{
	if (GetWorld() && GetWorld()->GetDemoNetDriver())
	{
		GetWorld()->GetWorldSettings()->DemoPlayTimeDilation = PlayRate;
	}
}

void UBaseReplaySubsystem::OnEnumerateStreamsComplete(const FEnumerateStreamsResult& StreamInfos)
{
	TArray<FSReplayInfo> AllReplays;

	for (FNetworkReplayStreamInfo StreamInfo : StreamInfos.FoundStreams)
	{
		if (!StreamInfo.bIsLive)
		{
			AllReplays.Add(FSReplayInfo(StreamInfo.Name, StreamInfo.FriendlyName, StreamInfo.Timestamp, StreamInfo.LengthInMS));
		}
	}
	BPOnFindReplaysComplete(AllReplays);
}

void UBaseReplaySubsystem::OnDeleteFinishedStreamComplete(const FDeleteFinishedStreamResult& DeleteResult)
{
	FindReplays();
}
